{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению\n",
    "Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center> Тема 6. Задача восстановления регрессии</center>\n",
    "## <center>Lasso- и Ridge-регрессия</center>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from cycler import cycler\n",
    "from matplotlib import pyplot as plt\n",
    "from sklearn.datasets import load_boston\n",
    "from sklearn.linear_model import Lasso, LassoCV, Ridge, RidgeCV\n",
    "from sklearn.model_selection import KFold, cross_val_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Будем работать с набором данных по ценам на дома в Бостоне (репозиторий UCI).**\n",
    "**Загружаем данные.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "boston = load_boston()\n",
    "X, y = boston[\"data\"], boston[\"target\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Почитаем описание набора данных:**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(boston.DESCR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Признаки:**\n",
    "- CRIM - количество преступлений на душу населения\n",
    "- ZN - процент жилых участков площадью больше 25 тыс. кв. футов (~ 23 сотки)\n",
    "- INDUS - процент площадей под оптовую торговлю \n",
    "- CHAS - протекает ли река\n",
    "- NOX - концентрация оксидов азота\n",
    "- RM - среднее число комнат в здании\n",
    "- AGE - доля зданий, построенных до 1940 года \n",
    "- DIS - взвешенное расстояние до 5 деловых центров Бостона\n",
    "- RAD - индекс доступности скоростных магистралей\n",
    "- TAX - уровень налогов\n",
    "- PTRATIO - среднее число учащихся на одного преподавателя \n",
    "- B - процент афроамериканцев\n",
    "- LSTAT - процент граждан с низким уровнем жизни\n",
    "- MEDV (целевой) - медианная стоимости домов в районе"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "boston.feature_names"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Посмотрим на первые 2 записи.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X[:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lasso-регрессия"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lasso-регрессия решает задачу минимизации среднеквардатичной ошибки с L1-регуляризацией:\n",
    "$$\\Large error(X, y, w) = \\frac{1}{2} \\sum_{i=1}^\\ell {(y_i - w^Tx_i)}^2 + \\alpha \\sum_{i=1}^d |w_i|$$\n",
    "\n",
    "где $y = w^Tx$ – уравнение гиперплоскости, зависящее от параметров модели $w$, $\\ell$-число объектов в выборке $X$, $d$ – число признаков, $y$ – значения целевого признака, $\\alpha$ – коэффициент регуляризации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обучим Lasso-регрессию с небольшим коэффициентом $\\alpha$ (слабая регуляризация). Обнуляется только коэффициент при признаке NOX (концентрация оксидов азота). Значит, он наименее важен для предсказания целевого признака – медианной стоимости домов в районе.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso = Lasso(alpha=0.1)\n",
    "lasso.fit(X, y)\n",
    "lasso.coef_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обучим Lasso-регрессию с коэффициентом $\\alpha=10$. Теперь ненулевые коэффициенты остались только при признаках ZN (процент жилых участков площадью больше 25 тыс. кв. футов), TAX (уровень налогов), B (процент афроамериканцев) и LSTAT (процент граждан с низким уровнем жизни).**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso = Lasso(alpha=10)\n",
    "lasso.fit(X, y)\n",
    "lasso.coef_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Таким образом, Lasso-регрессия служит методом отбора признаков.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_alphas = 200\n",
    "alphas = np.linspace(0.1, 10, n_alphas)\n",
    "model = Lasso()\n",
    "\n",
    "coefs = []\n",
    "for a in alphas:\n",
    "    model.set_params(alpha=a)\n",
    "    model.fit(X, y)\n",
    "    coefs.append(model.coef_)\n",
    "\n",
    "ax = plt.gca()\n",
    "ax.set_prop_cycle(cycler(\"color\", [\"b\", \"r\", \"g\", \"c\", \"k\", \"y\", \"m\"]))\n",
    "\n",
    "ax.plot(alphas, coefs)\n",
    "ax.set_xscale(\"log\")\n",
    "ax.set_xlim(ax.get_xlim()[::-1])  # reverse axis\n",
    "plt.xlabel(\"alpha\")\n",
    "plt.ylabel(\"weights\")\n",
    "plt.title(\"Lasso coefficients as a function of the regularization\")\n",
    "plt.axis(\"tight\")\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Теперь определим лучшее значение $\\alpha$ в процессе кросс-валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso_cv = LassoCV(alphas=alphas, cv=3, random_state=17)\n",
    "lasso_cv.fit(X, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso_cv.coef_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso_cv.alpha_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Метод `cross_val_score` максимизирует метрику, так что вместо \n",
    "минимизации MSE сделаем максимизацию отрицательного MSE – `neg_mean_squared_error`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cross_val_score(Lasso(lasso_cv.alpha_), X, y, cv=3, scoring=\"neg_mean_squared_error\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Чтобы все-таки трактовать результат в терминах MSE, выведем модуль среднего значения метрики `neg_mean_squared_error` на кросс-валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "abs(\n",
    "    np.mean(\n",
    "        cross_val_score(\n",
    "            Lasso(lasso_cv.alpha_), X, y, cv=3, scoring=\"neg_mean_squared_error\"\n",
    "        )\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "abs(np.mean(cross_val_score(Lasso(9.95), X, y, cv=3, scoring=\"neg_mean_squared_error\")))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Еще один неоднозначный момент: LassoCV сортирует значения параметров по убыванию – так проще оптимизировать. Из-за этого может показаться, что оптимизация параметра $\\alpha$ работает неправильно**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso_cv.alphas[:10]  # значения параметров на входе"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lasso_cv.alphas_[:10]  # преобразованные значения параметров"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(lasso_cv.alphas, lasso_cv.mse_path_.mean(1))  # неверно\n",
    "plt.axvline(lasso_cv.alpha_, c=\"g\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(lasso_cv.alphas_, lasso_cv.mse_path_.mean(1))  # верно\n",
    "plt.axvline(lasso_cv.alpha_, c=\"g\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ridge-регрессия"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ridge-регрессия (или гребневая регрессия) решает задачу минимизации среднеквардатичной ошибки с L2-регуляризацией:\n",
    "$$\\Large error(X, y, w) = \\frac{1}{2} \\sum_{i=1}^\\ell {(y_i - w^Tx_i)}^2 + \\alpha \\sum_{i=1}^d w_i^2$$\n",
    "\n",
    "где $y = w^Tx$ – уравнение гиперплоскости, зависящее от параметров модели $w$, $\\ell$-число объектов в выборке $X$, $d$ – число признаков, $y$ – значения целевого признака, $\\alpha$ – коэффициент регуляризации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В sklearn реализован специальный класс [RidgeCV](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.RidgeCV.html#sklearn.linear_model.RidgeCV) для кросс-валидации с Ridge-регрессией."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_alphas = 200\n",
    "ridge_alphas = np.logspace(-2, 6, n_alphas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ridge_cv = RidgeCV(alphas=ridge_alphas, scoring=\"neg_mean_squared_error\", cv=3)\n",
    "ridge_cv.fit(X, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ridge_cv.alpha_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**В случае Ridge-регрессии никакие праметры не зануляются – они могут быть очень малыми, но не нулевыми.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ridge_cv.coef_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_alphas = 200\n",
    "ridge_alphas = np.logspace(-2, 6, n_alphas)\n",
    "model = Ridge()\n",
    "\n",
    "coefs = []\n",
    "for a in alphas:\n",
    "    model.set_params(alpha=a)\n",
    "    model.fit(X, y)\n",
    "    coefs.append(model.coef_)\n",
    "\n",
    "ax = plt.gca()\n",
    "ax.set_prop_cycle(cycler(\"color\", [\"b\", \"r\", \"g\", \"c\", \"k\", \"y\", \"m\"]))\n",
    "\n",
    "ax.plot(alphas, coefs)\n",
    "ax.set_xscale(\"log\")\n",
    "ax.set_xlim(ax.get_xlim()[::-1])  # reverse axis\n",
    "plt.xlabel(\"alpha\")\n",
    "plt.ylabel(\"weights\")\n",
    "plt.title(\"Ridge coefficients as a function of the regularization\")\n",
    "plt.axis(\"tight\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ссылки\n",
    "- [Обощеннные линейные модели](http://scikit-learn.org/stable/modules/linear_model.html) (Generalized Linear Models, GLM) в Scikit-learn\n",
    "- [LinearRegression](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html#sklearn.linear_model.LinearRegression), [Lasso](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Lasso.html#sklearn.linear_model.Lasso), [LassoCV](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LassoCV.html#sklearn.linear_model.LassoCV), [Ridge](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html) и [RidgeCV](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.RidgeCV.html#sklearn.linear_model.RidgeCV) в Scikit-learn\n",
    "- [Статья](https://habrahabr.ru/post/264915/) \"Методы отбора фич\" на Хабрахабре с упоминанием Lasso-регрессии "
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  },
  "name": "lesson8_part1_kmeans.ipynb"
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
